﻿using Python.Runtime;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace eslib.py
{
    /// <summary>
    /// python运行引擎，可作为基类或者直接使用
    /// </summary>
    public class PyEngine
    {

        /// <summary>
        /// 
        /// </summary>
        public PyEngine()
        {
            Reset();
        }

        /// <summary>
        /// 重置
        /// </summary>
        private void Reset()
        {
            BindObjects = new List<KeyValuePair<string, object>>();
        }



        #region 运行方法

        /// <summary>
        /// 运行文件
        /// </summary>
        /// <param name="file"></param>
        /// <param name="dict"></param>
        public async Task ExecFile(string file, PyDict dict = null) => await Exec(File.ReadAllText(file), dict);


        /// <summary>
        /// 运行
        /// </summary>
        /// <param name="code"></param>
        /// <param name="dict"></param>
        /// <exception cref="Exception"></exception>
        public async Task Exec(string code, PyDict dict = null)
        {
            await Exec(scope =>
             {
                 scope.Exec(code, dict);
             });
        }


        /// <summary>
        /// 使用预编译运行
        /// </summary>
        /// <param name="compiledScript"></param>
        /// <param name="dict"></param>
        /// <exception cref="Exception"></exception>
        public async Task Exec(Func<PyObject> compiledScript, PyDict dict = null)
        {
            await Exec(scope =>
            {
                scope.Execute(compiledScript(), dict);
            });
        }


        /// <summary>
        /// 运行
        /// </summary>
        /// <param name="action"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task Exec(Action<PyScope> action)
        {
            await Invoke(() =>
            {
                PreInit();
                using (PyScope scope = Py.CreateScope())
                {
                    Init(scope);
                    action(scope);
                    PreDispose(scope);
                }
            });
        }


        #endregion


        #region 绑定对象

        /// <summary>
        /// 绑定对象集合
        /// </summary>
        public List<KeyValuePair<string, object>> BindObjects { get; private set; }

        /// <summary>
        /// 添加绑定对象
        /// </summary>
        /// <param name="name"></param>
        /// <param name="obj"></param>
        public void AddBindObject(string name, object obj)
            => BindObjects.Add(new KeyValuePair<string, object>(name, obj));

        #endregion


        #region 生命周期方法 

        /// <summary>
        /// 在GIL()后
        /// </summary>
        public virtual void PreInit() { }

        /// <summary>
        /// 初始化,在Py.GIL()之后.负责向py加载对象
        /// 如果派生类覆盖，需要base.Init()
        /// </summary>
        public virtual void Init(PyScope scope)
        {
            foreach (var bind in BindObjects)
                scope.Set(bind.Key, bind.Value);
        }

        /// <summary>
        /// 释放资源前(scope定义域内)
        /// </summary>
        public virtual void PreDispose(PyScope scope)
        {
        }

        #endregion


        #region 静态方法

        /// <summary>
        /// 预编译(只能在PreInit之中调用)
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public static PyObject Compile(string code)
        {
            return PythonEngine.Compile(code);
        }

        /// <summary>
        /// 预编译文件(只能在PreInit之中调用)
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public static PyObject CompileFile(string file) => Compile(File.ReadAllText(file));



        /// <summary>
        /// 直接在Py环境中执行
        /// </summary>
        /// <param name="action"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static async Task Invoke(Action action)
        {
            await Task.Run(() =>
            {
                try
                {
                    using (Py.GIL())
                    {
                        action();
                    }
                }
                catch (DllNotFoundException ex)
                {
                    throw new Exception("py运行引擎异常:" + ex.Message);
                }
            });

        }


        #endregion
    }
}
